package com.ccjk.beusoft.thirdlib.plugin1.hook;

import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.util.Log;

import com.ccjk.beusoft.thirdlib.plugin1.hook.handler.ActivityManagerHook;
import com.ccjk.beusoft.thirdlib.plugin1.hook.handler.HCallbackProxy;
import com.ccjk.beusoft.thirdlib.plugin1.hook.handler.PackageManagerHook;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created by CCJK on 2017/12/20.
 */

public class Hook {
    public static final String KEY_REAL_ACTIVITY = "RAW_ACTIVITY";

    public Context context;

    private Hook() {

    }

    private static class Instance {
        private static Hook instance = new Hook();
    }

    public static Hook get() {
        return Instance.instance;
    }

    private static Boolean isHook = false;

    public void hook(Context context) {
        if (isHook) return;
        if (this.context == null) this.context = context.getApplicationContext();
        hookAMS();
        hookPMS();
        hookActivityThreadHHandleMessage();
        isHook = true;
    }


    private void hookActivityThreadHHandleMessage() {
        try {
            Class activityThreadClass = Class.forName("android.app.ActivityThread");
            Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
            currentActivityThreadMethod.setAccessible(true);
            Object activityThread = currentActivityThreadMethod.invoke(null);

            Field mHField = activityThreadClass.getDeclaredField("mH");
            mHField.setAccessible(true);
            Handler mH = (Handler) mHField.get(activityThread);
            //
            Field mCallbackField = Handler.class.getDeclaredField("mCallback");
            mCallbackField.setAccessible(true);
            mCallbackField.set(mH, new HCallbackProxy(mH, context));

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

    }

    /**
     *
     */
    private void hookPMS() {
        try {
            Class activityThreadClass = Class.forName("android.app.ActivityThread");
            Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
            currentActivityThreadMethod.setAccessible(true);
            Object activityThread = currentActivityThreadMethod.invoke(null);
            //getPackageManager
            Method getPackageManagerMethod = activityThreadClass.getDeclaredMethod("getPackageManager");
            getPackageManagerMethod.setAccessible(true);
            Object iPackageManager = getPackageManagerMethod.invoke(null);
            //准备代理
            Object proxy = Proxy.newProxyInstance(activityThreadClass.getClassLoader(), new Class[]{
                            Class.forName("android.content.pm.IPackageManager")
                    },
                    new PackageManagerHook(context, iPackageManager));

            //1替换 activitythread sPackageManager
            Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");
            sPackageManagerField.setAccessible(true);
            sPackageManagerField.set(activityThread, proxy);

            //2.替换Context 中 ApplicationPackageManager mPm对象
            PackageManager packageManager = context.getPackageManager();
            Class clazz = Class.forName("android.app.ApplicationPackageManager");
            Field mPMField = clazz.getDeclaredField("mPM");
            mPMField.setAccessible(true);
            mPMField.set(packageManager, proxy);
            Log.e("hook pms ", "success");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

    }


    /**
     * lollipop,marshmallow
     */
    private void hookAMS() {
        try {
            Class activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
            Method getDefaultMethod = activityManagerNativeClass.getMethod("getDefault");
            getDefaultMethod.setAccessible(true);

            Object iActivityManager = getDefaultMethod.invoke(null);
            Object proxy = Proxy.newProxyInstance(activityManagerNativeClass.getClassLoader(),
                    new Class[]{Class.forName("android.app.IActivityManager")}, new ActivityManagerHook(context, iActivityManager));
            //
            Field gDefault = activityManagerNativeClass.getDeclaredField("gDefault");
            gDefault.setAccessible(true);
            Object sinleton = gDefault.get(null);
            Class singletonClass = Class.forName("android.util.Singleton");
            Field instanceField = singletonClass.getDeclaredField("mInstance");
            instanceField.setAccessible(true);
            instanceField.set(sinleton, proxy);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

    }


}
